package patternlearning;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringBufferInputStream;
import java.util.Vector;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.tools.ant.filters.StringInputStream;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;

import datastruct.document.BiomedDocument;

public class DocumentLearner {
	private BiomedDocument biodoc = new BiomedDocument();
	private Vector<ReasonPair> reasonVector = new Vector<ReasonPair>();
	private String groundTruthRoot = "data/gold/";
	private String parsedRoot = "output/parsed/i2b2/";
	private Vector<String> patternVector = new Vector<String>();
	public DocumentLearner(String filename)
	{
		this.ReadInFile(filename);
		this.ExtractPatterns();
	}
	public void ReadInFile(String filename)
	{
		this.biodoc.ReadFromParsedFile(this.parsedRoot+filename+".parsed");
		try {
			BufferedReader freader = new BufferedReader(new FileReader(this.groundTruthRoot+filename+".i2b2.entries"));
			String line;
			while((line=freader.readLine())!=null)
			{
				if(line.length()>0&&!line.contains("r=\"nm\"")&&!line.contains("ln=\"list\""))
				{
					ReasonPair pair = new ReasonPair();
					pair.ReadGrountTruthLine(line, filename);
					this.reasonVector.add(pair);
				}
			}
			freader.close();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public void ExtractPatterns()
	{
		int size = this.biodoc.getSize();
		for(int i=0;i<size;i++)
		{
			String sentence = biodoc.getSenetence(i);
			String syntreeStr = biodoc.getSyntaxTree(i);
			Node treeRoot = this.getSynTaxTree(syntreeStr);
			Node[] tokenNodes = this.getAllTokenNodes(treeRoot);
			int tokensize = tokenNodes.length;
			Node[][] nodeParentMatrix = new Node[tokensize][];
			for(int j=0;j<tokensize;j++)
			{
				nodeParentMatrix[j] = this.getParentNodesForOneNode(tokenNodes[j]);
			}
			for(int j=0;j<tokenNodes.length;j++)
				for(int k=0;k<tokenNodes.length;k++)
				{
					if(j==k) continue;
					String medName = tokenNodes[j].getFirstChild().getNodeValue();
					String reason = tokenNodes[k].getFirstChild().getNodeValue();
					if(this.isContainedPair(medName, reason))
					{
						this.getPatternForOnePair(nodeParentMatrix[j], nodeParentMatrix[k],medName,reason,sentence);
					}
				}
		}
	}
	private void getPatternForOnePair(Node[] entityNodeParents, Node[] attributeNodeParents,String medName,String reason,String sentence)
	{
		//Get the same parent node.
		int indexSameEntityNode = -1;
		int indexSameAttributeNode = -1;
		Node sameNode = null;
		for(int i=0;i<entityNodeParents.length;i++)
		{
			boolean ifGetSameNode = false;
			for(int j=0;j<attributeNodeParents.length;j++)
			{
				if(entityNodeParents[i].getNodeName().equals("#document")) break;
				NamedNodeMap attributeMap = entityNodeParents[i].getAttributes();
				Node idAttribute = attributeMap.getNamedItem("id");
				String entityParentID = idAttribute.getNodeValue();
				
				if(attributeNodeParents[j].getNodeName().equals("#document")) break;
				attributeMap = attributeNodeParents[j].getAttributes();
				idAttribute = attributeMap.getNamedItem("id");
				String attributeParentID = idAttribute.getNodeValue();
				if(entityParentID.equals(attributeParentID))
				{
					indexSameEntityNode = i;
					indexSameAttributeNode = j;
					sameNode = entityNodeParents[i];
					ifGetSameNode = true;
					break;
				}
			}
			if(ifGetSameNode) break;
		}
		
		
		String pattern = this.getNodeCat(sameNode)+"#";
		pattern += this.getNodeSequencePattern(entityNodeParents, indexSameEntityNode-1)+"#";
		pattern += this.getNodeSequencePattern(attributeNodeParents, indexSameAttributeNode-1);
		
		//pattern += "  #m="+medName+" r="+reason+" s="+sentence;
		
		if(!this.patternVector.contains(pattern)) this.patternVector.add(pattern);
	}
	private String getNodeSequencePattern(Node[] nodes,int sameIndex)
	{
		String pattern = "";
		for(int i=sameIndex;i>=0;i--)
		{
			String cat = this.getNodeCat(nodes[i]);
			String token = this.getFirstToken(nodes[i]);
			if(this.isPartternElement(cat, token)) 
			{
				String newElement = cat;
				if(this.isTokenNeededForPatternElement(cat, token)) newElement +=":"+token;
				newElement += " ";
				if(!pattern.endsWith(newElement))pattern+=newElement;
			}
		}
		return pattern;
	}
	
	private boolean isPartternElement(String cat,String token)
	{
		if(cat.endsWith("P")) return true;
		if(cat.equals("NX")) return true;
		return false;
	}
	private boolean isTokenNeededForPatternElement(String cat,String token)
	{
		if(cat.equals("NP")) return false;
		if(cat.endsWith("P")) return true;
		return false;
	}
	
	private String getFirstToken(Node root)
	{
		Node currentNode = root;
		while(true)
		{
			
			String cat = currentNode.getNodeName();
			if(cat.equals("tok")) break;
			else
			{
				for(currentNode = currentNode.getFirstChild();currentNode!=null;currentNode=currentNode.getNextSibling())
				{
					if(currentNode.getNodeType()==Node.ELEMENT_NODE) break;
				}
			}
		}
		return currentNode.getFirstChild().getNodeValue();
	}
	private String getNodeCat(Node node)
	{
		NamedNodeMap attributeMap = node.getAttributes();
		
		Node idAttribute = attributeMap.getNamedItem("cat");
		if(idAttribute==null) return "";
		return idAttribute.getNodeValue();
	}
	//Get all the nodes that is a token in the tree.
	private Node[] getAllTokenNodes(Node root)
	{
		Vector<Node> tokenNodeVector = new Vector<Node>();
		this.addTokenNodesToVector(root, tokenNodeVector);
		int size = tokenNodeVector.size();
		Node[] nodes = new Node[size];
		for(int i=0;i<size;i++)
		{
			nodes[i]=tokenNodeVector.elementAt(i);
		}
		return nodes;
	}
	private void addTokenNodesToVector(Node root, Vector<Node> tokenNodeVector)
	{
		if(root.getNodeType()!=Node.ELEMENT_NODE) return;
		if(root.getNodeName().equals("tok")) 
		{
			tokenNodeVector.add(root);
			return;
		}
		for(Node childNode = root.getFirstChild();childNode!=null;childNode = childNode.getNextSibling())
		{
			this.addTokenNodesToVector(childNode, tokenNodeVector);
		}
		
	}
	private Node getSynTaxTree(String syntaxTreeStr)
	{
		try {

			DocumentBuilderFactory domfac=DocumentBuilderFactory.newInstance();
			DocumentBuilder dombuilder;
			dombuilder = domfac.newDocumentBuilder();
			InputStream is=new StringInputStream(syntaxTreeStr);
			Document doc=dombuilder.parse(is);
			for(Node childNode = doc.getFirstChild();childNode!=null;childNode = childNode.getNextSibling())
			{
				if(childNode.getNodeType()==Node.ELEMENT_NODE) return childNode;
			}
		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SAXException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
		
	}
	private boolean isContainedPair(String entityName,String attributeName)
	{
		if(entityName.length()<=2||attributeName.length()<=2) return false;
		entityName = entityName.toLowerCase();
		attributeName = attributeName.toLowerCase();
		if(attributeName.equals("for")
				||attributeName.equals("as")
				||attributeName.equals("by")
				||attributeName.equals("with")
				||attributeName.equals("he")
				||attributeName.equals("him")
				||attributeName.equals("his")
				||attributeName.equals("she")
				||attributeName.equals("her")
				||attributeName.equals("we")
				||attributeName.equals("us")
				||attributeName.equals("our")) return false;
		if(entityName.equals("for")
				||entityName.equals("as")
				||entityName.equals("by")
				||entityName.equals("with")
				||entityName.equals("in")) return false;;
		for(ReasonPair pair : this.reasonVector)
		{
			String[] mTokens = pair.getMedName().replaceAll("\\.", "").split("\\s+");
			boolean mContains=false;
			for(String token : mTokens)
			{
				if(token.equals(entityName)) 
				{
					mContains = true;
					break;
				}
			}
			if(mContains==false) continue;
			String[] rTokens = pair.getReason().replaceAll("\\.", "").split("\\s+");
			boolean rContains=false;
			for(String token : rTokens)
			{
				if(token.equals(attributeName)) 
				{
					rContains = true;
					break;
				}
			}
			if(rContains) return true;
			
		}
		return false;
	}
	//Get a array of parents nodes for a node.
	private Node[] getParentNodesForOneNode(Node node)
	{
		Vector<Node> nodeVector = new Vector<Node>();
		Node currentNode = node.getParentNode();
		while(currentNode!=null)
		{
			nodeVector.add(currentNode);
			currentNode = currentNode.getParentNode();
		}
		int size = nodeVector.size();
		Node[] nodes = new Node[size];
		for(int i=0;i<size;i++)
		{
			nodes[i] = nodeVector.elementAt(i);
		}
		return nodes;
	}
	public String toString()
	{
		String str="";
		for(String line : this.patternVector)
		{
			str += line+"\n";
		}
		return str;
	}
	public Vector<String> getAllPatterns()
	{
		return this.patternVector;
	}
}
